Finding AIDS impact in r square

Finding information in data using statistical characteristics R-squared is a statistical measure of how close the data are to the fitted regression line

Inspired by

# VHS course "R"Kenntnisse um Wissen aus Daten zu gewinnen 2017
library(gapminder)
library(tidyverse)
library(plotly)

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout
tidyverse_packages()
 [1] "broom"     "dplyr"     "forcats"   "ggplot2"   "haven"     "httr"     
 [7] "hms"       "jsonlite"  "lubridate" "magrittr"  "modelr"    "purrr"    
[13] "readr"     "readxl"    "stringr"   "tibble"    "rvest"     "tidyr"    
[19] "xml2"      "tidyverse"

Peek into data

Plot data

summary(gapminder)
        country        continent        year         lifeExp           pop           
 Afghanistan:  12   Africa  :624   Min.   :1952   Min.   :23.60   Min.   :6.001e+04  
 Albania    :  12   Americas:300   1st Qu.:1966   1st Qu.:48.20   1st Qu.:2.794e+06  
 Algeria    :  12   Asia    :396   Median :1980   Median :60.71   Median :7.024e+06  
 Angola     :  12   Europe  :360   Mean   :1980   Mean   :59.47   Mean   :2.960e+07  
 Argentina  :  12   Oceania : 24   3rd Qu.:1993   3rd Qu.:70.85   3rd Qu.:1.959e+07  
 Australia  :  12                  Max.   :2007   Max.   :82.60   Max.   :1.319e+09  
 (Other)    :1632                                                                    
   gdpPercap       
 Min.   :   241.2  
 1st Qu.:  1202.1  
 Median :  3531.8  
 Mean   :  7215.3  
 3rd Qu.:  9325.5  
 Max.   :113523.1  
                   
gapminder %>% filter(gdpPercap < 300)
# Print xy plot -----------------------------------------------------------
gapminder <- gapminder %>% mutate(year1950 = year -1950)
ggplot(gapminder, aes(x=year, y=lifeExp)) +geom_line(aes(group = country))

Data wrangling

lets transform the data so that we keep all of a country’s data in one row

  • build nested data.frame
  • access elements of nested data.frame
# Nested data -------------------------------------------------------------
by_country <- gapminder  %>%
  group_by(continent, country) %>%
  nest()
by_country                  # show content of df
str(by_country[1:3,])       # show structure of first 3 rows of grouped and nested df
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   3 obs. of  3 variables:
 $ continent: Factor w/ 5 levels "Africa","Americas",..: 3 4 1
 $ country  : Factor w/ 142 levels "Afghanistan",..: 1 2 3
 $ data     :List of 3
  ..$ :Classes ‘tbl_df’, ‘tbl’ and 'data.frame':    12 obs. of  5 variables:
  .. ..$ year     : int  1952 1957 1962 1967 1972 1977 1982 1987 1992 1997 ...
  .. ..$ lifeExp  : num  28.8 30.3 32 34 36.1 ...
  .. ..$ pop      : int  8425333 9240934 10267083 11537966 13079460 14880372 12881816 13867957 16317921 22227415 ...
  .. ..$ gdpPercap: num  779 821 853 836 740 ...
  .. ..$ year1950 : num  2 7 12 17 22 27 32 37 42 47 ...
  ..$ :Classes ‘tbl_df’, ‘tbl’ and 'data.frame':    12 obs. of  5 variables:
  .. ..$ year     : int  1952 1957 1962 1967 1972 1977 1982 1987 1992 1997 ...
  .. ..$ lifeExp  : num  55.2 59.3 64.8 66.2 67.7 ...
  .. ..$ pop      : int  1282697 1476505 1728137 1984060 2263554 2509048 2780097 3075321 3326498 3428038 ...
  .. ..$ gdpPercap: num  1601 1942 2313 2760 3313 ...
  .. ..$ year1950 : num  2 7 12 17 22 27 32 37 42 47 ...
  ..$ :Classes ‘tbl_df’, ‘tbl’ and 'data.frame':    12 obs. of  5 variables:
  .. ..$ year     : int  1952 1957 1962 1967 1972 1977 1982 1987 1992 1997 ...
  .. ..$ lifeExp  : num  43.1 45.7 48.3 51.4 54.5 ...
  .. ..$ pop      : int  9279525 10270856 11000948 12760499 14760787 17152804 20033753 23254956 26298373 29072015 ...
  .. ..$ gdpPercap: num  2449 3014 2551 3247 4183 ...
  .. ..$ year1950 : num  2 7 12 17 22 27 32 37 42 47 ...
by_country$data[[1]]        # show data of first row
by_country$data[1]          # show data of first row
[[1]]
by_country$data[[1]][[2]]   # show  contents of second column 
 [1] 28.801 30.332 31.997 34.020 36.088 38.438 39.854 40.822 41.674 41.763 42.129
[12] 43.828
by_country$data[[1]][2]     # show second column
  • [ returns a list
  • [[ returns content of list

a good explanation can be found at: http://r4ds.had.co.nz/vectors.html#lists

structure of dataframe

The column data is a list of data frames. Therefore, we have now a row per country and all data of that country in a dataframe in one column.

In a grouped dataframe each row is an oberservation, in a nested dataframe each row is a group, in this case, a group of a country’s observations.

Build a model

Lets build a model for each country, lifeExp ~ year

# Fit models --------------------------------------------------------------
country_model <- function(df){
  lm(lifeExp  ~  year1950, data=df)  # use year1950 because the absolute value is not important
}
models <- by_country %>%
  mutate(
    model = map(data, country_model)
  )
models
lm(lifeExp  ~  year1950, data=by_country$data[[1]]) 

Call:
lm(formula = lifeExp ~ year1950, data = by_country$data[[1]])

Coefficients:
(Intercept)     year1950  
    29.3566       0.2753  
models$model[1]
[[1]]

Call:
lm(formula = lifeExp ~ year1950, data = df)

Coefficients:
(Intercept)     year1950  
    29.3566       0.2753  

Combine data wrangling and modell building

# Put it all together -----------------------------------------------------
by_country <- gapminder  %>%
  group_by(continent, country) %>%
  nest() %>%  
  mutate(
    model = map(data, country_model)
  )
by_country

Get the model data in a tidy form using the broom package

models <- models %>%
  mutate(
    glance  = map(model, broom::glance),        # Construct a single row summary "glance" of a model
    rsq     = glance %>% map_dbl("r.squared"),  # note the pipe within mutate(...)
    tidy    = map(model, broom::tidy),          # Tidy the result of a test into a summary data.frame
    augment = map(model, broom::augment)
  )
models
models$glance[1]
[[1]]
models$rsq[1]
[1] 0.9477123
models$tidy[1]
[[1]]
models$augment[1]
[[1]]
NA

Investigate how well the model fits

models %>% 
  ggplot(aes(rsq, country)) +
  geom_point(aes(colour = continent)) 

# source("gapminderShiny.R")

is the plot clear? how could it be improved?

ggplot orders categorical variables alphabetically

models %>% 
  ggplot(aes(rsq, reorder(country, rsq))) +
  geom_point(aes(colour = continent)) 

Find the countries with the worst fit

  models %>% filter((rsq<0.1 & rsq>0))  %>% unnest(rsq)  %>% top_n(6,rsq) %>% unnest(data) %>% 
    ggplot(aes(year, lifeExp)) +
    geom_line(aes( alpha = 1/3))  +
    facet_wrap(~country)

NA

How much did the countries improve life expectancy over time

# Unnest data -------------------------------------------------------------
unnest(models, data)
unnest(models, glance, .drop = TRUE) 
unnest(models, tidy)
# Plot data frame ---------------------------------------------------------
plotLife <- models %>%
  unnest(tidy) %>%
  select(continent, country, term, estimate, rsq) %>%
  spread(term, estimate) %>%
  ggplot(aes(`(Intercept)`,year1950))+
  geom_point(aes(colour = continent, size = rsq, fill = country)) +
  geom_smooth(se=FALSE) +
  xlab("Life expectancy (1950)") +
  ylab("Yearly improvement") +
  scale_size_area() + guides(fill=FALSE)
ggplotly(plotLife, tooltip = c("year1950", "country"))
`geom_smooth()` using method = 'loess'

where are the exeptions

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Cmd+Option+I.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Cmd+Shift+K to preview the HTML file).

LS0tCnRpdGxlOiAiR2FwbWluZGVyIGZvciBWSFMgMS8yMDE3IgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICBodG1sX25vdGVib29rOiBkZWZhdWx0Ci0tLQoKIyBGaW5kaW5nIEFJRFMgaW1wYWN0IGluIHIgc3F1YXJlCgpGaW5kaW5nIGluZm9ybWF0aW9uIGluIGRhdGEgdXNpbmcgc3RhdGlzdGljYWwgY2hhcmFjdGVyaXN0aWNzIAoqKlItc3F1YXJlZCoqIGlzIGEgc3RhdGlzdGljYWwgbWVhc3VyZSBvZiBob3cgY2xvc2UgdGhlIGRhdGEgYXJlIHRvIHRoZSBmaXR0ZWQgcmVncmVzc2lvbiBsaW5lCgoqKkluc3BpcmVkIGJ5ICoqCgotIFRFRCB0YWxrIG9mICAqKkhhbnMgUm9zbGluZyoqOiBMZXQgbXkgZGF0YXNldCBjaGFuZ2UgeW91ciBtaW5kc2V0Cmh0dHBzOi8vd3d3LnRlZC5jb20vdGFsa3MvaGFuc19yb3NsaW5nX2F0X3N0YXRlI3QtMTE3NjQwNwoKLSAqKkhhZGxleSBXaWNraGFtKiogTWFuYWdpbmcgbWFueSBtb2RlbHMgd2l0aCBSIGh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL3dhdGNoP3Y9cnozX0ZEVnQ5ZWcgCgoKCgpgYGB7cn0KCmxpYnJhcnkoZ2FwbWluZGVyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShwbG90bHkpCnRpZHl2ZXJzZV9wYWNrYWdlcygpCmBgYAoKIyMgUGVlayBpbnRvIGRhdGEKIyMjIFBsb3QgZGF0YQogCmBgYHtyfQpzdW1tYXJ5KGdhcG1pbmRlcikKZ2FwbWluZGVyICU+JSBmaWx0ZXIoZ2RwUGVyY2FwIDwgMzAwKQojIFByaW50IHh5IHBsb3QgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCgpnYXBtaW5kZXIgPC0gZ2FwbWluZGVyICU+JSBtdXRhdGUoeWVhcjE5NTAgPSB5ZWFyIC0xOTUwKQpnZ3Bsb3QoZ2FwbWluZGVyLCBhZXMoeD15ZWFyLCB5PWxpZmVFeHApKSArZ2VvbV9saW5lKGFlcyhncm91cCA9IGNvdW50cnkpKQoKYGBgCgojIyMgRGF0YSB3cmFuZ2xpbmcKbGV0cyB0cmFuc2Zvcm0gdGhlIGRhdGEgc28gdGhhdCB3ZSBrZWVwIGFsbCBvZiBhIGNvdW50cnkncyBkYXRhIGluIG9uZSByb3cKCi0gYnVpbGQgbmVzdGVkIGRhdGEuZnJhbWUKLSBhY2Nlc3MgZWxlbWVudHMgb2YgbmVzdGVkIGRhdGEuZnJhbWUKCmBgYHtyfQoKIyBOZXN0ZWQgZGF0YSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpieV9jb3VudHJ5IDwtIGdhcG1pbmRlciAgJT4lCiAgZ3JvdXBfYnkoY29udGluZW50LCBjb3VudHJ5KSAlPiUKICBuZXN0KCkKCmJ5X2NvdW50cnkgICAgICAgICAgICAgICAgICAjIHNob3cgY29udGVudCBvZiBkZgpzdHIoYnlfY291bnRyeVsxOjMsXSkgICAgICAgIyBzaG93IHN0cnVjdHVyZSBvZiBmaXJzdCAzIHJvd3Mgb2YgZ3JvdXBlZCBhbmQgbmVzdGVkIGRmCmJ5X2NvdW50cnkkZGF0YVtbMV1dICAgICAgICAjIHNob3cgZGF0YSBvZiBmaXJzdCByb3cKYnlfY291bnRyeSRkYXRhWzFdICAgICAgICAgICMgc2hvdyBkYXRhIG9mIGZpcnN0IHJvdwpieV9jb3VudHJ5JGRhdGFbWzFdXVtbMl1dICAgIyBzaG93ICBjb250ZW50cyBvZiBzZWNvbmQgY29sdW1uIApieV9jb3VudHJ5JGRhdGFbWzFdXVsyXSAgICAgIyBzaG93IHNlY29uZCBjb2x1bW4KYGBgCgoKLSBbIHJldHVybnMgYSBsaXN0Ci0gW1sgcmV0dXJucyBjb250ZW50IG9mIGxpc3QKCmEgZ29vZCBleHBsYW5hdGlvbiBjYW4gYmUgZm91bmQgYXQ6IGh0dHA6Ly9yNGRzLmhhZC5jby5uei92ZWN0b3JzLmh0bWwjbGlzdHMgCgojIyMgc3RydWN0dXJlIG9mIGRhdGFmcmFtZQoKVGhlIGNvbHVtbiAqKmRhdGEqKiBpcyBhIGxpc3Qgb2YgZGF0YSBmcmFtZXMuIFRoZXJlZm9yZSwgd2UgaGF2ZSBub3cgYSByb3cgcGVyIGNvdW50cnkgYW5kIGFsbCBkYXRhIG9mIHRoYXQgY291bnRyeSBpbiBhIGRhdGFmcmFtZSBpbiAqKm9uZSoqIGNvbHVtbi4KCkluIGEgZ3JvdXBlZCBkYXRhZnJhbWUgKiplYWNoIHJvdyBpcyBhbiBvYmVyc2VydmF0aW9uKiosIGluIGEgbmVzdGVkIGRhdGFmcmFtZSAqKmVhY2ggcm93IGlzIGEgZ3JvdXAqKiwgaW4gdGhpcyBjYXNlLCBhIGdyb3VwIG9mIGEgY291bnRyeeKAmXMgb2JzZXJ2YXRpb25zLgoKCgojIyBCdWlsZCBhIG1vZGVsCkxldHMgYnVpbGQgYSBtb2RlbCBmb3IgZWFjaCBjb3VudHJ5LCBsaWZlRXhwIH4gIHllYXIKCgpgYGB7cn0KCiMgRml0IG1vZGVscyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKY291bnRyeV9tb2RlbCA8LSBmdW5jdGlvbihkZil7CiAgbG0obGlmZUV4cCAgfiAgeWVhcjE5NTAsIGRhdGE9ZGYpICAjIHVzZSB5ZWFyMTk1MCBiZWNhdXNlIHRoZSBhYnNvbHV0ZSB2YWx1ZSBpcyBub3QgaW1wb3J0YW50Cn0KbW9kZWxzIDwtIGJ5X2NvdW50cnkgJT4lCiAgbXV0YXRlKAogICAgbW9kZWwgPSBtYXAoZGF0YSwgY291bnRyeV9tb2RlbCkKICApCm1vZGVscwpsbShsaWZlRXhwICB+ICB5ZWFyMTk1MCwgZGF0YT1ieV9jb3VudHJ5JGRhdGFbWzFdXSkgCm1vZGVscyRtb2RlbFsxXQoKYGBgCgojIyMgQ29tYmluZSBkYXRhIHdyYW5nbGluZyBhbmQgbW9kZWxsIGJ1aWxkaW5nCgpgYGB7cn0KCiMgUHV0IGl0IGFsbCB0b2dldGhlciAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKYnlfY291bnRyeSA8LSBnYXBtaW5kZXIgICU+JQogIGdyb3VwX2J5KGNvbnRpbmVudCwgY291bnRyeSkgJT4lCiAgbmVzdCgpICU+JSAgCiAgbXV0YXRlKAogICAgbW9kZWwgPSBtYXAoZGF0YSwgY291bnRyeV9tb2RlbCkKICApCgpieV9jb3VudHJ5CmBgYAoKCgojIyBHZXQgdGhlIG1vZGVsIGRhdGEgaW4gYSB0aWR5IGZvcm0gdXNpbmcgdGhlICoqYnJvb20qKiBwYWNrYWdlCgpgYGB7cn0KbW9kZWxzIDwtIG1vZGVscyAlPiUKICBtdXRhdGUoCiAgICBnbGFuY2UgID0gbWFwKG1vZGVsLCBicm9vbTo6Z2xhbmNlKSwgICAgICAgICMgQ29uc3RydWN0IGEgc2luZ2xlIHJvdyBzdW1tYXJ5ICJnbGFuY2UiIG9mIGEgbW9kZWwKICAgIHJzcSAgICAgPSBnbGFuY2UgJT4lIG1hcF9kYmwoInIuc3F1YXJlZCIpLCAgIyBub3RlIHRoZSBwaXBlIHdpdGhpbiBtdXRhdGUoLi4uKQogICAgdGlkeSAgICA9IG1hcChtb2RlbCwgYnJvb206OnRpZHkpLCAgICAgICAgICAjIFRpZHkgdGhlIHJlc3VsdCBvZiBhIHRlc3QgaW50byBhIHN1bW1hcnkgZGF0YS5mcmFtZQogICAgYXVnbWVudCA9IG1hcChtb2RlbCwgYnJvb206OmF1Z21lbnQpCiAgKQptb2RlbHMKbW9kZWxzJGdsYW5jZVsxXQptb2RlbHMkcnNxWzFdCm1vZGVscyR0aWR5WzFdCm1vZGVscyRhdWdtZW50WzFdCmBgYAoKIyMjIEludmVzdGlnYXRlIGhvdyB3ZWxsIHRoZSBtb2RlbCBmaXRzIAoKCmBgYHtyfQoKbW9kZWxzICU+JSAKICBnZ3Bsb3QoYWVzKHJzcSwgY291bnRyeSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBjb250aW5lbnQpKSAKIyBzb3VyY2UoImdhcG1pbmRlclNoaW55LlIiKQpgYGAKCmlzIHRoZSBwbG90IGNsZWFyPyBob3cgY291bGQgaXQgYmUgaW1wcm92ZWQ/CgoKCmdncGxvdCBvcmRlcnMgY2F0ZWdvcmljYWwgdmFyaWFibGVzIGFscGhhYmV0aWNhbGx5IAoKYGBge3J9Cgptb2RlbHMgJT4lIAogIGdncGxvdChhZXMocnNxLCByZW9yZGVyKGNvdW50cnksIHJzcSkpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gY29udGluZW50KSkgCgpgYGAKCiMjIyBGaW5kIHRoZSBjb3VudHJpZXMgd2l0aCB0aGUgd29yc3QgZml0CgoKYGBge3J9CgogIG1vZGVscyAlPiUgZmlsdGVyKChyc3E8MC4xICYgcnNxPjApKSAgJT4lIHVubmVzdChyc3EpICAlPiUgdG9wX24oNixyc3EpICU+JSB1bm5lc3QoZGF0YSkgJT4lIAogICAgZ2dwbG90KGFlcyh5ZWFyLCBsaWZlRXhwKSkgKwogICAgZ2VvbV9saW5lKGFlcyggYWxwaGEgPSAxLzMpKSAgKwogICAgZmFjZXRfd3JhcCh+Y291bnRyeSkKICAKYGBgCgojIEhvdyBtdWNoIGRpZCB0aGUgY291bnRyaWVzIGltcHJvdmUgbGlmZSBleHBlY3RhbmN5IG92ZXIgdGltZQoKCmBgYHtyfQoKIyBVbm5lc3QgZGF0YSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgp1bm5lc3QobW9kZWxzLCBkYXRhKQp1bm5lc3QobW9kZWxzLCBnbGFuY2UsIC5kcm9wID0gVFJVRSkgCnVubmVzdChtb2RlbHMsIHRpZHkpCgojIFBsb3QgZGF0YSBmcmFtZSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCgpwbG90TGlmZSA8LSBtb2RlbHMgJT4lCiAgdW5uZXN0KHRpZHkpICU+JQogIHNlbGVjdChjb250aW5lbnQsIGNvdW50cnksIHRlcm0sIGVzdGltYXRlLCByc3EpICU+JQogIHNwcmVhZCh0ZXJtLCBlc3RpbWF0ZSkgJT4lCiAgZ2dwbG90KGFlcyhgKEludGVyY2VwdClgLHllYXIxOTUwKSkrCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gY29udGluZW50LCBzaXplID0gcnNxLCBmaWxsID0gY291bnRyeSkpICsKICBnZW9tX3Ntb290aChzZT1GQUxTRSkgKwogIHhsYWIoIkxpZmUgZXhwZWN0YW5jeSAoMTk1MCkiKSArCiAgeWxhYigiWWVhcmx5IGltcHJvdmVtZW50IikgKwogIHNjYWxlX3NpemVfYXJlYSgpICsgZ3VpZGVzKGZpbGw9RkFMU0UpCmdncGxvdGx5KHBsb3RMaWZlLCB0b29sdGlwID0gYygieWVhcjE5NTAiLCAiY291bnRyeSIpKQoKCgpgYGAKIyMgd2hlcmUgYXJlIHRoZSBleGVwdGlvbnMKCmBgYHtyfQoKdW5uZXN0KG1vZGVscywgYXVnbWVudCkgIyB1c2luZyB0aGUgdW5uZXN0IHdpdGggb25lIGF1Z21lbnQgYXJndW1lbnQga2VlcHMgdGhlIGdyb3VwaW5nIHZhcmlhYmxlcyBhbmQgYXVnbWVudCAKCm1vZGVscyAlPiUgdW5uZXN0KGF1Z21lbnQpICU+JSAKICBnZ3Bsb3QoYWVzKHllYXIxOTUwLCAucmVzaWQpKSArCiAgZ2VvbV9saW5lKGFlcyhncm91cCA9IGNvdW50cnkpLCBhbHBoYSA9IDEvMykgKwogIGdlb21fc21vb3RoKHNlID0gRkFMU0UpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBjb2xvdXIgPSAid2hpdGUiKSArCiAgZmFjZXRfd3JhcCh+Y29udGluZW50LCBzY2FsZXMgPSAiZnJlZV95IikgCgoKYGBgCgpBZGQgYSBuZXcgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpJbnNlcnQgQ2h1bmsqIGJ1dHRvbiBvbiB0aGUgdG9vbGJhciBvciBieSBwcmVzc2luZyAqQ21kK09wdGlvbitJKi4KCldoZW4geW91IHNhdmUgdGhlIG5vdGVib29rLCBhbiBIVE1MIGZpbGUgY29udGFpbmluZyB0aGUgY29kZSBhbmQgb3V0cHV0IHdpbGwgYmUgc2F2ZWQgYWxvbmdzaWRlIGl0IChjbGljayB0aGUgKlByZXZpZXcqIGJ1dHRvbiBvciBwcmVzcyAqQ21kK1NoaWZ0K0sqIHRvIHByZXZpZXcgdGhlIEhUTUwgZmlsZSku